﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Configuration;
using System.Web;
using System.IO;
using System.Xml.Serialization;

namespace Altairis.Sewen.PageStore {
    public class FilePageStoreProvider : PageStoreProvider {

        public string DataFolderPath { get; private set; }

        // Initialization and configuration

        public override void Initialize(string name, NameValueCollection config) {
            // Base init
            base.Initialize(name, config);

            // Get data folder path
            this.DataFolderPath = config.GetConfigValue("dataFolderPath", "~/App_Data/Sewen/");
            if (this.DataFolderPath.StartsWith("~/")) this.DataFolderPath = HttpContext.Current.Server.MapPath(this.DataFolderPath);
            if (!Directory.Exists(this.DataFolderPath)) {
                try {
                    Directory.CreateDirectory(this.DataFolderPath);
                }
                catch (Exception ex) {
                    throw new ConfigurationErrorsException(string.Format("Configured data folder '{0}' was not found and could not be created.", this.DataFolderPath), ex);
                }
            }

            // Throw error on excess attributes
            if (config.Count != 0) throw new ConfigurationErrorsException("Unrecognized configuration attributes found: " + string.Join(", ", config.AllKeys));
        }

        // Provider implementation
        public override bool DeletePage(string name) {
            // Validate arguments
            if (name == null) throw new ArgumentNullException("name");
            if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "name");

            // Find files
            var slug = SewenPage.GetSlug(name);
            var pageFiles = new DirectoryInfo(this.DataFolderPath).EnumerateFiles(slug + ".*.xml");
            var deleted = false;
            foreach (var pageFile in pageFiles) {
                deleted = true;
                pageFile.Delete();
            }
            return deleted;
        }

        public override bool DeletePage(string name, DateTime version) {
            // Validate arguments
            if (name == null) throw new ArgumentNullException("name");
            if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "name");

            // Find files
            var slug = SewenPage.GetSlug(name);
            var fileName = Path.Combine(this.DataFolderPath, string.Format("{0}.{1:yyyyMMddHHmmssfff}.xml", slug, version));
            if (!File.Exists(fileName)) return false;
            File.Delete(fileName);
            return true;
        }

        public override void SavePage(SewenPage page) {
            // Validate arguments
            if (page == null) throw new ArgumentNullException("page");
            if (string.IsNullOrWhiteSpace(page.Title)) throw new ArgumentException("Page title cannot be empty or whitespace only string.", "page");
            if (string.IsNullOrWhiteSpace(page.Body)) throw new ArgumentException("Page body cannot be empty or whitespace only string.", "page");

            // Check if there is change against last known version
            var lastKnownPage = LoadPage(page.Slug);
            if (lastKnownPage != null && lastKnownPage.Body.Equals(page.Body)) return; // no changes made

            // Set parameters
            page.Version = DateTime.Now;
            page.SetContextInfo();

            // Save page file
            var fileName = Path.Combine(this.DataFolderPath, string.Format("{0}.{1:yyyyMMddHHmmssfff}.xml", page.Slug, page.Version));
            using (var f = File.Create(fileName)) {
                var xs = new XmlSerializer(page.GetType());
                xs.Serialize(f, page);
            }
        }

        public override SewenPage LoadPage(string name, DateTime version) {
            // Validate arguments
            if (string.IsNullOrWhiteSpace(name)) return null;

            // Get all versions of this page
            var slug = SewenPage.GetSlug(name);
            var pageInfos = from f in new DirectoryInfo(this.DataFolderPath).EnumerateFiles(slug + ".*.xml")
                            select new {
                                FileInfo = f,
                                Version = DateTime.ParseExact(f.Name.Split('.')[1], "yyyyMMddHHmmssfff", null)
                            };

            // Find latest version available on specific time
            var pi = pageInfos.OrderByDescending(x => x.Version).FirstOrDefault(x => x.Version <= version);
            if (pi == null) return null; // not found

            // Return page
            var xs = new XmlSerializer(typeof(SewenPage));
            using (var f = pi.FileInfo.OpenRead()) {
                return xs.Deserialize(f) as SewenPage;
            }
        }

        public override IEnumerable<SewenPage> LoadPageVersions(string name) {
            // Validate arguments
            if (name == null) throw new ArgumentNullException("name");
            if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "name");

            // Return all page versions
            var slug = SewenPage.GetSlug(name);
            var pageFiles = new DirectoryInfo(this.DataFolderPath).EnumerateFiles(slug + ".*.xml");
            var xs = new XmlSerializer(typeof(SewenPage));
            foreach (var pageFile in pageFiles) {
                using (var f = pageFile.OpenRead()) {
                    yield return xs.Deserialize(f) as SewenPage;
                }
            }
        }

        public override IEnumerable<SewenPage> FindPagesByTitle(string searchString, StringSearchMode mode) {
            throw new NotImplementedException("Search is not supported by this provider");
        }



    }
}
